﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Xml.Serialization;
using System.Xml;
using System.IO;

namespace Vibz.Common.Channel
{
    public class SocketPublisher : ISocketPublisher
    {
        int _lastPosition = 0;
        private System.Net.Sockets.Socket _socket;
        AsyncCallback _dataTransferCallBack;
        public bool IsConnected { get { return (_socket != null); } }
        public System.Net.Sockets.Socket Socket
        {
            get
            {
                if (_socket == null)
                    throw new Exception("Socket is not open.");
                return _socket;
            }
            internal set { _socket = value; }
        }

        public event DataReceiveHandler DataReceived;
        public delegate void DataReceiveHandler(System.Net.Sockets.Socket socket, byte[] receivedData, EventArgs e);
        static object _lock = new object();
        void OnDataReceived(IAsyncResult asyn)
        {
            try
            {
                var socketPacket = (SocketPacket)asyn.AsyncState;

                var receivedData = socketPacket.GetData(asyn);
                if (String.IsNullOrEmpty(receivedData))
                    return;
                _lastPosition += receivedData.Length;

                if (!ResolveDataReciept(socketPacket.Socket, receivedData) && DataReceived != null)
                {
                    DataReceived(socketPacket.Socket, Encoding.ASCII.GetBytes(receivedData), null);
                }
                WaitForData(socketPacket.Socket);
            }
            catch (ObjectDisposedException)
            {
                Console.WriteLine("OnConnectRequest: Socket has been closed.");
                throw;
            }
            catch (SocketException ex)
            {
                Console.WriteLine(string.Format("Error occured: {0}", ex.Message));
            }
        }
        internal virtual bool ResolveDataReciept(System.Net.Sockets.Socket socket, string receivedData)
        {
            return false;
        }

        public void SendMessage(string message)
        {
            var messageBytes =
                   System.Text.Encoding.ASCII.GetBytes(message);
            _socket.Send(messageBytes);
        }
        public void SendMessage(DataPacket obj)
        {
            if (obj == null)
                return;
            var messageBytes = obj.Serialize();
            _socket.Send(messageBytes);
        }
        internal byte[] GetMessage(string messageID)
        {
            return System.Text.Encoding.ASCII.GetBytes(GetMessageText(messageID));
        }
        internal string GetMessageText(string messageID)
        {
            SendMessage(messageID);
            try
            {
                var socketPacket = new SocketPacket(Socket);
                byte[] msgBytes = socketPacket.DataBuffer;
                int numberOfBytesReceived = Socket.Receive(msgBytes, 0, Socket.ReceiveBufferSize, SocketFlags.None);
                return Encoding.ASCII.GetString(msgBytes, 0, numberOfBytesReceived);
            }
            catch (SocketException ex)
            {
                Console.WriteLine(
                     string.Format("Error occured: {0}", ex.Message));
                throw;
            }
        }
        public void WaitForData(System.Net.Sockets.Socket dataTransferSocket)
        {
            try
            {
                if (_dataTransferCallBack == null)
                { _dataTransferCallBack = OnDataReceived; }
                var socketPacket = new SocketPacket(dataTransferSocket);
                dataTransferSocket.BeginReceive(socketPacket.DataBuffer, 0,
                              socketPacket.DataBuffer.Length, SocketFlags.None,
                             _dataTransferCallBack, socketPacket);
            }
            catch (SocketException ex)
            {
                Console.WriteLine(
                     string.Format("Error occured: {0}", ex.Message));
                Console.Read();
            }
        }
    }
}
